﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using ColorTools;
using DevelopersToolbox;
using DevelopersToolbox.Util;

namespace ColorTools {
    public partial class ColorControl : UserControl {
        private bool _isUpdating;
        private const int _captureWidth = 15;
        private const int _captureHeight = 15;
        private bool _isDragging;
        private readonly Cursor _bullsEyeCursor;

        public ColorControl() {
            InitializeComponent();
            _colorFinder.Width = _colorFinder.Width - (_colorFinder.Width % _captureWidth);
            _colorFinder.Height = _colorFinder.Height - (_colorFinder.Height % _captureHeight);
            _colorTheoryType.SelectedIndex = 0;
            _bullsEyeCursor = new Cursor(GetType().Assembly.GetManifestResourceStream("ColorTools.BullsEye.cur"));
        }

        private void _red_TextChanged(object sender, EventArgs e) {
            try {
                SetColorRGB((string) _red.Text, _green.Text, _blue.Text);
            }
            catch {
                _red.ForeColor = Color.Red;
            }
        }

        private void _green_TextChanged(object sender, EventArgs e) {
            try {
                SetColorRGB((string) _red.Text, _green.Text, _blue.Text);
            }
            catch {
                _green.ForeColor = Color.Red;
            }
        }

        private void _blue_TextChanged(object sender, EventArgs e) {
            try {
                SetColorRGB((string) _red.Text, _green.Text, _blue.Text);
            }
            catch {
                _blue.ForeColor = Color.Red;
            }
        }

        private void _html_TextChanged(object sender, EventArgs e) {
            try {
                SetColorHTML(_html.Text);
            }
            catch {
                _html.ForeColor = Color.Red;
            }
        }

        private void _hue_TextChanged(object sender, EventArgs e) {
            try {
                SetColorHSL((string) _hue.Text, _sat.Text, _lum.Text);
            }
            catch {
                _hue.ForeColor = Color.Red;
            }
        }

        private void _sat_TextChanged(object sender, EventArgs e) {
            try {
                SetColorHSL((string) _hue.Text, _sat.Text, _lum.Text);
            }
            catch {
                _sat.ForeColor = Color.Red;
            }
        }

        private void _lum_TextChanged(object sender, EventArgs e) {
            try {
                SetColorHSL((string) _hue.Text, _sat.Text, _lum.Text);
            }
            catch {
                _lum.ForeColor = Color.Red;
            }
        }

        public void SetColorRGB(string red, string green, string blue) {
            SetColorRGB(int.Parse(red), int.Parse(green), int.Parse(blue));
        }

        public void SetColorHSL(string hue, string sat, string lum) {
            SetColorHSL(int.Parse(hue), int.Parse(sat), int.Parse(lum));
        }

        public void SetColorHTML(string html) {
            html = html.Trim(new char[] { '#', ' ', '\t' });
            string redOctet = html.Substring(0, 2);
            string greenOctet = html.Substring(2, 2);
            string blueOctet = html.Substring(4, 2);
            int red = Convert.ToInt32(redOctet, 16);
            int green = Convert.ToInt32(greenOctet, 16);
            int blue = Convert.ToInt32(blueOctet, 16);
            SetColorRGB(red, green, blue);
            _red.Text = red.ToString();
            _green.Text = green.ToString();
            _blue.Text = blue.ToString();
            ClearErrors();
        }

        private void SetColorRGB(int red, int green, int blue) {
            if (!_isUpdating) {
                try {
                    _isUpdating = true;
                    if (red < 0 || red > DevColor.RGBMAX)
                        throw new Exception("Number out of range.");
                    if (green < 0 || green > DevColor.RGBMAX)
                        throw new Exception("Number out of range.");
                    if (blue < 0 || blue > DevColor.RGBMAX)
                        throw new Exception("Number out of range.");

                    DevColor clr = DevColor.FromRGB(red, green, blue);
                    _hue.Text = clr.Hue.ToString();
                    _sat.Text = clr.Sat.ToString();
                    _lum.Text = clr.Lum.ToString();
                    _html.Text = clr.HTML;
                    ClearErrors();
                }
                finally {
                    _isUpdating = false;
                }
            }
        }

        private void SetColorHSL(int hue, int sat, int lum) {
            if (!_isUpdating) {
                try {
                    _isUpdating = true;
                    if (hue < 0 || hue > DevColor.HLSMAX)
                        throw new Exception("Number out of range.");
                    if (sat < 0 || sat > DevColor.HLSMAX)
                        throw new Exception("Number out of range.");
                    if (lum < 0 || lum > DevColor.HLSMAX)
                        throw new Exception("Number out of range.");

                    DevColor clr = DevColor.FromHLS(hue, sat, lum);
                    _red.Text = clr.Red.ToString();
                    _green.Text = clr.Green.ToString();
                    _blue.Text = clr.Blue.ToString();
                    _html.Text = clr.HTML;
                    ClearErrors();
                }
                finally {
                    _isUpdating = false;
                }
            }
        }

        private void ClearErrors() {
            _red.ForeColor = Color.Black;
            _green.ForeColor = Color.Black;
            _blue.ForeColor = Color.Black;
            _hue.ForeColor = Color.Black;
            _sat.ForeColor = Color.Black;
            _lum.ForeColor = Color.Black;
            _html.ForeColor = Color.Black;
            UpdateColorTheory();
            _colorPreview.Refresh();
            _hueSatMap.SetHueSat(Hue, Sat);
            _lumMap.SetColor(Hue, Sat, Lum);
        }

        private void _colorPreview_Paint(object sender, PaintEventArgs e) {
            using (Brush brush = new SolidBrush(Color.FromArgb(Red, Green, Blue))) {
                e.Graphics.FillRectangle(brush, 0, 0, _colorPreview.Width, _colorPreview.Height);
            }
        }

        private int Red {
            get { return int.Parse(_red.Text); }
        }

        private int Green {
            get { return int.Parse(_green.Text); }
        }

        private int Blue {
            get { return int.Parse(_blue.Text); }
        }

        private int Hue {
            get { return int.Parse(_hue.Text); }
        }

        private int Sat {
            get { return int.Parse(_sat.Text); }
        }

        private int Lum {
            get { return int.Parse(_lum.Text); }
        }

        private void ColorControl_MouseUp(object sender, MouseEventArgs e) {
            if (_isDragging) {
                _isDragging = false;
                Cursor = Cursors.Default;
                this.Capture = false;
            }
        }

        private void ColorControl_MouseMove(object sender, MouseEventArgs e) {
            if (_isDragging) {
                Point mouseScreenPt = PointToScreen(e.Location);
                int srcX = mouseScreenPt.X - ((_captureWidth + 2) / 2);
                int srcY = mouseScreenPt.Y - ((_captureHeight + 2) / 2);

                int hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow());
                int hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
                int hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, _captureWidth + 2, _captureHeight + 2);
                GDI32.SelectObject(hdcDest, hBitmap);
                GDI32.BitBlt(hdcDest, 0, 0, _captureWidth + 2, _captureHeight + 2, hdcSrc, srcX, srcY, 0x00CC0020);

                _colorFinder.SetImage(Image.FromHbitmap(new IntPtr(hBitmap)));

                // cleanup
                User32.ReleaseDC(User32.GetDesktopWindow(), hdcSrc);
                GDI32.DeleteDC(hdcDest);
                GDI32.DeleteObject(hBitmap);

                Color color = _colorFinder.GetColorAt(_captureWidth / 2, _captureHeight / 2);
                _red.Text = color.R.ToString();
                _green.Text = color.G.ToString();
                _blue.Text = color.B.ToString();
                SetColorRGB(Red, Green, Blue);
            }
        }

        private void UpdateColorTheory() {
            IList<DevColor> colors = null;
            DevColor currentColor = DevColor.FromRGB(Red, Green, Blue);
            switch (_colorTheoryType.Text) {
                case "Compimentary":
                    colors = ColorTheory.Compimentary(currentColor);
                    break;
                case "Monocromatic":
                    colors = ColorTheory.Monocromatic(currentColor);
                    break;
                case "Analogous":
                    colors = ColorTheory.Analogous(currentColor);
                    break;
                case "Triadic":
                    colors = ColorTheory.Triadic(currentColor);
                    break;
                case "Tetradic":
                    colors = ColorTheory.Tetradic(currentColor);
                    break;
                case "Split Complimentary":
                    colors = ColorTheory.SplitComplimentary(currentColor);
                    break;
            }
            if (colors != null) {
                SetColorTheory(_colorTheory1, colors.Count >= 1 ? colors[0] : null);
                SetColorTheory(_colorTheory2, colors.Count >= 2 ? colors[1] : null);
                SetColorTheory(_colorTheory3, colors.Count >= 3 ? colors[2] : null);
                SetColorTheory(_colorTheory4, colors.Count >= 4 ? colors[3] : null);
                SetColorTheory(_colorTheory5, colors.Count >= 5 ? colors[4] : null);
                SetColorTheory(_colorTheory6, colors.Count >= 6 ? colors[5] : null);
            }
        }

        private void SetColorTheory(TextBox colorTheoryTextBox, DevColor color) {
            if (color == null) {
                colorTheoryTextBox.BackColor = Color.White;
                colorTheoryTextBox.Text = "";
            }
            else {
                colorTheoryTextBox.BackColor = color.Color;
                if (color.Lum < DevColor.HLSMAX / 2) {
                    colorTheoryTextBox.ForeColor = Color.White;
                }
                else {
                    colorTheoryTextBox.ForeColor = Color.Black;
                }
                colorTheoryTextBox.Text = string.Format("{0}\r\n{1},{2},{3}", color.HTML, color.Red, color.Green, color.Blue);
            }
        }

        private void _dragMe_MouseMove(object sender, MouseEventArgs e) {
            if (!_isDragging && e.Button == MouseButtons.Left) {
                _isDragging = true;
                Cursor = _bullsEyeCursor;
                this.Capture = true;
            }
        }

        private void _hueSatMap_HueSatChange(object sender, HueSatChangeArgs e) {
            _hue.Text = e.Hue.ToString();
            _sat.Text = e.Sat.ToString();
            SetColorHSL(Hue, Sat, Lum);
        }

        private void _lumMap_LumChange(object sender, LumChangeEventArgs e) {
            _lum.Text = e.Lum.ToString();
            SetColorHSL(Hue, Sat, Lum);
        }

        private void _colorFinder_ColorSelected(object sender, ColorSelectedEventArgs e) {
            _red.Text = e.Color.R.ToString();
            _green.Text = e.Color.G.ToString();
            _blue.Text = e.Color.B.ToString();
            SetColorRGB(Red, Green, Blue);
        }

        private void _colorTheoryType_SelectedIndexChanged(object sender, EventArgs e) {
            UpdateColorTheory();
        }
    }
}